1 module hip.api.graphics.g2d.animation;
2 public import hip.api.renderer.texture;
3 public import hip.api.graphics.color;
4 public import hip.api.data.textureatlas;
5 
6 /**
7 *   The frame user is responsible for using the frame properties, while the track is responsible
8 *   for returning the correct frame
9 */
10 struct HipAnimationFrame
11 {
12     IHipTextureRegion region;
13     HipColor color = HipColor.white;
14     ///X, Y
15     float[2] offset = [0,0];
16 
17     version(Have_util)
18     {
19         public import hip.util.data_structures:Array2D, Array2D_GC;
20         static HipAnimationFrame[] fromTextureRegions(Array2D!IHipTextureRegion reg, uint startY, uint startX, uint endY, uint endX)
21         {
22             HipAnimationFrame[] ret;
23 
24             for(int i = startY; i <= endY; i++)
25                 for(int j = startX; j <= endX; j++)
26                     ret~= HipAnimationFrame(reg[i,j]);
27             return ret;
28         }
29         static HipAnimationFrame[] fromTextureRegions(Array2D_GC!IHipTextureRegion reg, uint startY, uint startX, uint endY, uint endX)
30         {
31             HipAnimationFrame[] ret;
32 
33             for(int i = startY; i <= endY; i++)
34                 for(int j = startX; j <= endX; j++)
35                     ret~= HipAnimationFrame(reg[i,j]);
36             return ret;
37         }
38     }
39 }
40 
41 enum HipAnimationLoopingMode
42 {
43     none,
44     reset,
45     pingpong
46 }
47 
48 
49 interface IHipAnimationTrack
50 {   
51     string name() const;
52     HipAnimationLoopingMode loopingMode() const;
53     HipAnimationLoopingMode loopingMode(HipAnimationLoopingMode mode = HipAnimationLoopingMode.reset);
54 
55     bool reverse() const;
56     bool reverse(bool setReverse);
57     ///Returns how many seconds the animation lasts
58     float getDuration() const;
59 
60     IHipAnimationTrack addFrames(HipAnimationFrame[] frame...);
61     IHipAnimationTrack addFrames(IHipTextureRegion[] regions...);
62     version(Have_util)
63     {
64         import hip.util.data_structures:Array2D_GC;
65         final IHipAnimationTrack addFrames(Array2D_GC!IHipTextureRegion sheet)
66         {
67             scope IHipTextureRegion[] regions;
68             for(int y = 0; y < sheet.getHeight; y++)
69                 for(int x = 0; x < sheet.getWidth; x++)
70                     regions~= sheet[y,x];
71             return addFrames(regions);
72         }
73     }
74     void reset();
75     void setFrame(uint frame);
76     void setFramesPerSecond(uint framesPerSecond);
77     HipAnimationFrame* getFrameForTime(float time);
78     HipAnimationFrame* getFrameForProgress(float progress);
79     HipAnimationFrame* update(float deltaTime);
80 }
81 
82 
83 version(ScriptAPI) version = DefineCreateFromAtlas;
84 
85 interface IHipAnimation
86 {
87     IHipAnimationTrack getCurrentTrack();
88     HipAnimationFrame* getCurrentFrame();
89     final IHipTextureRegion getCurrentRegion()
90     {
91         HipAnimationFrame* frame = getCurrentFrame();
92         if(frame == null)
93             return null;
94         return frame.region;
95     }
96 
97     version(Have_util) version(DefineCreateFromAtlas)
98     {
99         /**
100         *   Creates an IHipAnimation from a loaded texture atlas.
101         *   Its frames will be checked such as `mySprite${frameNumber}`.
102         *   The animation will be named as the string without the number.
103         */
104         static IHipAnimation createFromAtlas(IHipTextureAtlas atlas, string animationName, uint framesPerSecond = 24)
105         {
106             import hip.util.string:getNumericEnding, lastIndexOf;
107             import hip.util.algorithm;
108             import hip.api.graphics.g2d.renderer2d;
109             import std.algorithm:sort;
110             
111             IHipAnimation anim = newHipAnimation(animationName);
112             foreach(string frameName; sort(atlas.frames.keys))
113             {
114                 AtlasFrame* frame = frameName in atlas;
115                 string name = frameName;
116                 int index = frameName.lastIndexOf(frameName.getNumericEnding);
117                 if(index != -1)
118                     name = frameName[0..index];
119                 IHipAnimationTrack track = anim.getTrack(name);
120                 if(track is null)
121                 {
122                     anim.addTrack(track = newHipAnimationTrack(name, framesPerSecond, HipAnimationLoopingMode.reset));
123                 }
124                 track.addFrames(HipAnimationFrame(frame.region, HipColor.white, [0,0]));
125 
126             }
127             return anim;
128         }
129     }
130 
131 
132     final string getCurrentTrackName() {return getCurrentTrack().name;}
133     IHipAnimation addTrack(IHipAnimationTrack track);
134     void update(float deltaTime);
135     void setTimeScale(float scale);
136     void play(string trackName);
137     IHipAnimationTrack getTrack(string trackName);
138     
139 }